package org.truenewx.tnxjee.core.config;

import javax.annotation.*;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.truenewx.tnxjee.core.crypto.EncryptKeyProvider;
import org.truenewx.tnxjee.core.crypto.KeyBidirectionalEncryptor;
import org.truenewx.tnxjee.core.util.LogUtil;
import org.truenewx.tnxjee.core.util.SpringUtil;

public abstract class EncryptPropertyValueTranslator implements ConfigPropertyValueTranslator, InitializingBean {

    @Autowired
    protected EncryptPropertyValueTranslatorProperties properties;
    @Autowired
    protected ApplicationContext context;
    private EncryptKeyProvider keyProvider;
    private KeyBidirectionalEncryptor encryptor;

    @Override
    public void afterPropertiesSet() throws Exception {
        this.encryptor = newEncryptor();
    }

    protected abstract KeyBidirectionalEncryptor newEncryptor();

    @Override
    public <T> T translate(Object propertyValue) {
        return decrypt(propertyValue, getKey());
    }

    @SuppressWarnings("unchecked")
    public <T> T decrypt(Object propertyValue, Object key) {
        if (propertyValue instanceof String) {
            String value = (String) propertyValue;
            String prefix = getPrefix();
            if (StringUtils.isNotBlank(prefix) && StringUtils.startsWith(value, prefix)) {
                String encrypted = value.substring(prefix.length());
                if (!encrypted.isEmpty()) {
                    try {
                        return (T) this.encryptor.decrypt(encrypted, key);
                    } catch (Exception e) {
                        LogUtil.error(getClass(), "====== '{}' is not a valid encrypted text ======", encrypted);
                    }
                }
            }
            return (T) propertyValue;
        }
        return null;
    }

    public final String getPrefix() {
        String prefix = this.properties.getPrefix();
        if (StringUtils.isBlank(prefix)) {
            prefix = getDefaultPrefix();
        }
        return prefix;
    }

    @Nonnull
    protected String getDefaultPrefix() {
        return "enc:";
    }

    protected final Object getKey() {
        Object key = null;
        if (this.keyProvider == null) {
            Class<? extends EncryptKeyProvider> keyProviderClass = this.properties.getKeyProviderClass();
            if (keyProviderClass != null) {
                this.keyProvider = SpringUtil.getFirstBeanByClass(this.context, keyProviderClass);
            }
        }
        if (this.keyProvider != null) {
            key = this.keyProvider.getKey();
        }
        if (key == null) {
            key = this.properties.getKey();
            if (key == null) {
                this.properties.setKey("key");
                key = this.properties.getKey();
            }
        }
        return key;
    }

    public String encrypt(String s, Object key) {
        if (key == null) {
            key = getKey();
        }
        return getPrefix() + this.encryptor.encrypt(s, key);
    }

}
